#ifndef __QTCH_TCP_SERVER_H__
#define __QTCH_TCP_SERVER_H__

#include <memory>
#include <vector>
#include <map>
#include <string>
#include "noncopyable.h"
#include "socket.h"
#include "iomanager.h"
#include "config.h"

namespace qtch{


struct TcpServerConf {
    typedef std::shared_ptr<TcpServerConf> ptr;
    std::vector<std::string> address;
    int keeplive = 0;
    int timeout = 2 * 60 * 2000;
    int ssl = 0;
    std::string id;
    std::string type = "http";
    std::string cert_file;
    std::string name;
    std::string key_file;
    std::string accept_work;
    std::string io_work;
    std::string process_work;
    std::map<std::string,std::string> args;

    bool isInvalid(){
        if(!address.empty()){
            return false;
        }
        if(!ssl&&(cert_file.empty()||key_file.empty())){
            return false;
        }
        return true;
    }

    bool operator==(const TcpServerConf& oth) const {
        return address == oth.address
            && keeplive == oth.keeplive
            && timeout == oth.timeout
            && ssl == oth.ssl
            && id == oth.id
            && type == oth.type
            && cert_file == oth.cert_file
            && key_file == oth.key_file
            && accept_work == oth.accept_work
            && io_work == oth.io_work
            && process_work == oth.process_work
            && args == oth.args;
    }


};

template<>
class LexicalCast<TcpServerConf,std::string> {
public:
    TcpServerConf operator()(const std::string& v){
        TcpServerConf conf;
        YAML::Node node = YAML::Load(v);
        if(!node.IsMap()){
            return conf;
        }
        conf.id = node["id"].as<std::string>(conf.id);
        conf.keeplive = node["keeplive"].as<int>(conf.keeplive);
        conf.timeout = node["timeout"].as<int>(conf.timeout);
        conf.ssl = node["ssl"].as<int>(conf.ssl);
        conf.type = node["type"].as<std::string>(conf.type);
        conf.cert_file = node["cert_file"].as<std::string>(conf.cert_file);
        conf.name = node["name"].as<std::string>(conf.name);
        conf.key_file = node["key_file"].as<std::string>(conf.key_file);
        conf.accept_work = node["accept_work"].as<std::string>(conf.accept_work);
        conf.io_work = node["io_work"].as<std::string>(conf.io_work);
        conf.process_work = node["process_work"].as<std::string>(conf.process_work);
        conf.args = qtch::LexicalCast< std::map<std::string,std::string>,std::string>()(node["args"].as<std::string>(""));
        if(node["address"].IsDefined()){
            for(size_t i = 0; i<node["address"].size();++i){
                conf.address.push_back(node["address"][i].as<std::string>());
            }
        }
        return conf;
        
    }
};

template<>
class LexicalCast<std::string,TcpServerConf> {
public:
    std::string operator()(const TcpServerConf&conf){
        YAML::Node node;
        node["id"] = conf.id;
        node["keeplive"] = conf.keeplive;
        node["timeout"] = conf.timeout;
        node["ssl"] = conf.ssl;
        node["type"] = conf.type;
        node["cert_file"] = conf.cert_file;
        node["name"] = conf.name;
        node["key_file"] = conf.key_file;
        node["accept_work"] = conf.accept_work;
        node["io_work"] = conf.io_work;
        node["process_work"] = conf.process_work;
        node["args"] = LexicalCast<std::string,std::map<std::string,std::string> >()(conf.args);
        for(size_t i = 0; i<conf.address.size(); ++i){
            node["addres"].push_back(conf.address[i]);
        }
        std::stringstream ss;
        ss << node;
        return ss.str();
    }
};


class TcpServer : public std::enable_shared_from_this<TcpServer> , Noncopyable {
public:
    typedef std::shared_ptr<TcpServer> ptr;
    TcpServer(qtch::IOManager* worker = IOManager::getThis()
             ,qtch::IOManager* ioWorker = IOManager::getThis()
             ,qtch::IOManager* acceptWorker = IOManager::getThis());
    virtual ~TcpServer();
    virtual bool bind(qtch::Address::ptr addr, bool ssl = false);
    virtual bool bind(const std::vector<Address::ptr>& addrs
                     ,std::vector<Address::ptr>& fails
                     ,bool ssl = false);
    virtual bool start();
    virtual void stop();

    uint64_t getRecvTimeout() const {return m_recvTimeout;}
    std::string getName() const {return m_name;}
    void setRecvTimeout(uint64_t v) {m_recvTimeout = v;}
    void setName(std::string& v) {m_name = v;}
    bool isStop()  const {return m_isStop;}
    virtual std::string toString(const std::string& prefix = "");
    std::vector<Socket::ptr> getSocket() const {return m_socks;}
    const TcpServerConf::ptr& getConf() const {return m_conf;}
    void setConf(const TcpServerConf& conf) {m_conf.reset(new TcpServerConf(conf));}

    bool loadCertificates(const std::string& cert_file, const std::string& key_file);

protected:
    virtual void handleClient(Socket::ptr client);
    virtual void startAccpet(Socket::ptr sock);

protected:
    std::vector<Socket::ptr> m_socks;
    IOManager* m_worker;
    IOManager* m_acceptWorker;
    IOManager* m_ioWorker;
    uint64_t m_recvTimeout;
    std::string m_name;
    std::string m_type = "tcp";
    bool m_isStop;
    bool m_ssl = false;
    TcpServerConf::ptr m_conf;
};



}



#endif